package com.nexus.all.es.service.impl;

import cn.hutool.core.collection.ListUtil;
import com.nexus.all.dto.NoteQueryDTO;
import com.nexus.all.es.repository.EsNoteRepository;
import com.nexus.all.es.model.EsNote;
import com.nexus.all.es.model.EsNoteContent;
import com.nexus.all.es.service.EsNoteService;
import com.nexus.all.service.NoteService;
import com.nexus.all.vo.NoteVO;
import com.nexus.bean.query.QueryResultVO;
import com.nexus.bean.utils.BeanUtils;
import com.nexus.bean.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
@AllArgsConstructor
public class EsNoteServiceImpl implements EsNoteService {
    private final NoteService noteService;
    private final EsNoteRepository esNoteRepository;
    private final ElasticsearchRestTemplate elasticsearchRestTemplate;


    @Override
    public Integer dealSyncNoteToEs() {
        NoteQueryDTO dto = new NoteQueryDTO();
        dto.setPageNo(1);
        dto.setPageSize(1000);

        Integer total = 0;
        Boolean isHasNext = Boolean.TRUE;
        while (isHasNext) {
            QueryResultVO<NoteVO> resultVO = noteService.queryNotes(dto);
            List<NoteVO> records = resultVO.getRecords();

            log.info("已同步:{}", total);

            isHasNext = resultVO.getHasNextPage();
            dto.setPageNo(dto.getPageNo() + 1);

            List<EsNote> esNoteList = new ArrayList<>();
            for (NoteVO record : records) {
                EsNote esNote = BeanUtils.beanCopy(record, EsNote.class);
                esNote.setEsNoteContent(BeanUtils.beanCopy(record.getNotecontent(), EsNoteContent.class));
                esNote.setCreateTime(record.getCreateTime().toEpochSecond(ZoneOffset.UTC));
                esNoteList.add(esNote);
            }

            Iterable<EsNote> noteIterable = esNoteRepository.saveAll(esNoteList);
            Iterator<EsNote> iterator = noteIterable.iterator();
            while (iterator.hasNext()) {
                total++;
                iterator.next();
            }
        }
        return total;
    }


    /*@Override
    public Page<EsNote> search(NoteQueryDTO dto) {
        Pageable pageable = PageRequest.of(dto.getPageNo(), dto.getPageSize());
        Page<EsNote> esNotes = esNoteRepository.findByTitleContains(dto.getKeyword(), pageable);
        esNotes.getContent().forEach(content -> {
            //时间转换
            Instant instant = Instant.ofEpochSecond(content.getCreateTime());
            content.setCreateDateTime(instant.atZone(ZoneOffset.UTC).toLocalDateTime());
        });
        return esNotes;
    }*/


    @Override
    public Page<EsNote> search(NoteQueryDTO dto) {

        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //设置分页
        PageRequest pageable = PageRequest.of(dto.getPageNo(), dto.getPageSize());
        nativeSearchQueryBuilder.withPageable(pageable);

        //过滤
        if (Objects.nonNull(dto.getUserId()) || Objects.nonNull(dto.getNoteId())) {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            if (Objects.nonNull(dto.getUserId())) {
                boolQueryBuilder.must(QueryBuilders.termQuery("userId", dto.getUserId()));
            }
            if (Objects.nonNull(dto.getNoteId())) {
                boolQueryBuilder.must(QueryBuilders.termQuery("noteId", dto.getNoteId()));
            }
            nativeSearchQueryBuilder.withFilter(boolQueryBuilder);
        }

        //搜素
        if (StringUtils.isBlank(dto.getKeyword())) {
            nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
        } else {
            //模糊查询匹配关键字
            nativeSearchQueryBuilder.withQuery(QueryBuilders.wildcardQuery("title", "*" + dto.getKeyword() + "*"));
            /*List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
            filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.fuzzyQuery("title", dto.getKeyword()).fuzziness(Fuzziness.build("AUTO:4,6")),
                    ScoreFunctionBuilders.weightFactorFunction(10)));
            filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("title2", dto.getKeyword()),
                    ScoreFunctionBuilders.weightFactorFunction(10)));
            nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(keyword,"name","subTitle","keywords"));
            FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()];
            filterFunctionBuilders.toArray(builders);
            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders)
                    .scoreMode(FunctionScoreQuery.ScoreMode.SUM)
                    .setMinScore(2);
            nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder);*/
        }

        //排序
        //按相关度
        nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
        //按创建时间
        nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC));

        NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
        log.info("DSL:{}", searchQuery.getQuery());

        SearchHits<EsNote> searchHits = elasticsearchRestTemplate.search(searchQuery, EsNote.class);
        if (searchHits.getTotalHits() <= 0) {
            return new PageImpl<>(ListUtil.empty(), pageable, 0);
        }
        List<EsNote> searchProductList = searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
        for (EsNote esNote : searchProductList) {
            Instant instant = Instant.ofEpochSecond(esNote.getCreateTime());
            esNote.setCreateDateTime(instant.atZone(ZoneOffset.UTC).toLocalDateTime());
        }
        return new PageImpl<>(searchProductList, pageable, searchHits.getTotalHits());
    }
}
